/*
 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.nio;

import java.util.Spliterator;

/**
 * 存储特定基础类型数据的容器。
 *
 * <p> 缓冲区是特定基础类型的线性有限序列。
 * 除了它的内容外，buffer的基本特性是它的capacity, limit, position：
 * </p>
 *
 * <blockquote>
 *
 *   <p> 缓冲区的capacity是它包含的元素的数量。
 *   缓冲区的容量永远不会是负的，也不会改变。
 *   </p>
 *
 *   <p> 缓冲区的limit是第一个不应该读或写的元素的索引。
 *   缓冲区的限制永远不会是负数，也不会大于其容量。
 *   </p>
 *
 *   <p> 缓冲区的position是下一个要读取或写入的元素的索引。
 *   缓冲区的位置永远不会是负数，也不会超过它的limit。
 *   </p>
 *
 * </blockquote>
 *
 * <p> 每个非布尔基础类型都有一个子类。
 *
 *
 * <h2> 传输数据 </h2>
 *
 * <p> 此类的每个子类都定义了两类get和put操作</p>
 *
 * <blockquote>
 *
 *   <p>相对操作，读取或写入从当前位置开始的一个或多个元素，然后按传输的元素数增加该位置。
 *   如果请求的传输超过limit，则相对的get操作抛出BufferUnderflowException，
 *   而相对的put操作抛出BufferOverflowException；无论哪种情况，都不会传输数据。
 *   </p>
 *
 *   <p> 绝对操作采用显式元素索引，不会影响position。
 *   如果index参数超过限制，绝对的get和put操作会抛出IndexOutOfBoundsException。
 *   </p>
 *
 * </blockquote>
 *
 * <p> 当然，也可以通过一个合适的channel的IO操作，
 * 将数据传送进或者出，缓存中一个相对于当前position的位置。
 *
 *
 * <h2> 标记和重置 </h2>
 *
 * <p> 缓冲区的mark是在调用reset方法时其位置将被重置到的索引。
 * 标记不是总是被定义的，但是当它被定义时，它就不是负的，也永远不会比position大。
 * 如果标记已定义，则当位置或限制调整为小于标记的值时，将丢弃该标记。
 * 如果未定义标记，则调用reset方法将引发InvalidMarkException。
 *
 *
 * <h2> 不变式</h2>
 *
 * <p> 以下不变量适用于mark, position, limit和capacity值：
 *
 * <blockquote>
 *     <tt>0</tt> <tt>&lt;=</tt>
 *     <i>mark</i> <tt>&lt;=</tt>
 *     <i>position</i> <tt>&lt;=</tt>
 *     <i>limit</i> <tt>&lt;=</tt>
 *     <i>capacity</i>
 * </blockquote>
 *
 * <p> 新创建的缓冲区总是position为0，mark未定义。
 * 初始limit可以是零，也可以是其他值，这取决于缓冲区的类型和它的构造方式。
 * 新分配的缓冲区的每个元素初始化为零。
 *
 *
 * <h2> Clearing, flipping, and rewinding </h2>
 *
 * <p> 除了访问位置、限制和容量值以及标记和重置的方法外，此类还定义了对缓冲区的以下操作：
 *
 * <ul>
 *
 *   <li><p>clear使缓冲区为新的通道read或相对put操作做好准备：
 *   它将limit设置为capacity，position设置为0。
 *   </p></li>
 *
 *   <li><p> flip使缓冲区为新的通道写入或相对get操作序列做好准备：
 *   它将limit设置为当前position，position设置为0。
 *   </p></li>
 *
 *   <li><p>rewind使缓冲区准备好重新读取已包含的数据：
 *   它保持limit不变，position设置为0。
 *   </p></li>
 *
 * </ul>
 *
 *
 * <h2> 只读缓冲区 </h2>
 *
 * <p> 每个缓冲区都是可读的，但不是每个缓冲区都是可写的。
 * 每个buffer类的改变内容的方法被指定为可选操作，当对只读缓冲区进行调用时，将抛出ReadOnlyBufferException。
 * 只读缓冲区不允许更改其内容，但其标记、位置和限制值是可变的。
 * 是否缓冲区是否为只读可以通过调用其isReadOnly方法来确定。
 *
 *
 * <h2> 线程安全 </h2>
 *
 * <p>  多个并发线程使用缓冲区不安全。
 * 如果缓冲将由多个线程使用，那么应该通过适当的同步来控制对缓冲区的访问。
 *
 *
 * <h2> 调用链 </h2>
 *
 * <p> 类中没有返回值的方法被指定为返回调用它们的缓冲区。
 * 这允许链接方法调用；例如
 *
 * <blockquote><pre>
 * b.flip();
 * b.position(23);
 * b.limit(42);</pre></blockquote>
 *
 * 可以用一个更紧凑的语句代替
 *
 * <blockquote><pre>
 * b.flip().position(23).limit(42);</pre></blockquote>
 *
 *
 * @author Mark Reinhold
 * @author JSR-51 Expert Group
 * @since 1.4
 */

public abstract class Buffer {

    /**
     * 在缓冲区中遍历和拆分元素的Spliterators的特性。
     */
    static final int SPLITERATOR_CHARACTERISTICS =
        Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;

    // 不等式: mark <= position <= limit <= capacity
    private int mark = -1;
    private int position = 0;
    private int limit;
    private int capacity;

    // 仅用于直接缓冲区
    // 注意:在JNI的GetDirectBufferAddress中为速度在这里被提升
    long address;

    // 在检查不变量之后，使用给定的标记、位置、限制和容量创建一个新的缓冲区。
    Buffer(int mark, int pos, int lim, int cap) {       // package-private
        if (cap < 0)
            throw new IllegalArgumentException("Negative capacity: " + cap);
        // 设置四大属性
        this.capacity = cap;
        limit(lim);
        position(pos);
        if (mark >= 0) {
            if (mark > pos)
                throw new IllegalArgumentException("mark > position: ("
                                                   + mark + " > " + pos + ")");
            this.mark = mark;
        }
    }

    /**
     * 返回缓冲区的容量。
     *
     * @return  The capacity of this buffer
     */
    public final int capacity() {
        return capacity;
    }

    /**
     * 返回缓冲区的位置。
     *
     * @return  The position of this buffer
     */
    public final int position() {
        return position;
    }

    /**
     * 设置缓冲区的位置。
     * 如果标记已定义且大于新位置，则丢弃该标记。
     *
     * @param  newPosition
     *         The new position value; must be non-negative
     *         and no larger than the current limit
     *
     * @return  This buffer
     *
     * @throws  IllegalArgumentException
     *          If the preconditions on <tt>newPosition</tt> do not hold
     */
    public final Buffer position(int newPosition) {
        if ((newPosition > limit) || (newPosition < 0))
            throw new IllegalArgumentException();
        position = newPosition;
        if (mark > position) mark = -1;
        return this;
    }

    /**
     * 返回缓冲区的限制。
     *
     * @return  The limit of this buffer
     */
    public final int limit() {
        return limit;
    }

    /**
     * 设置缓冲区的限制。
     * 如果位置大于新的限制，那么它被设置为新的限制。
     * 如果标记被定义并且大于新的限制，那么它将被丢弃。
     *
     * @param  newLimit
     *         The new limit value; must be non-negative
     *         and no larger than this buffer's capacity
     *
     * @return  This buffer
     *
     * @throws  IllegalArgumentException
     *          If the preconditions on <tt>newLimit</tt> do not hold
     */
    public final Buffer limit(int newLimit) {
        if ((newLimit > capacity) || (newLimit < 0))
            throw new IllegalArgumentException();
        limit = newLimit;
        if (position > limit) position = limit;
        if (mark > limit) mark = -1;
        return this;
    }

    /**
     * 将缓冲区的标记设置在它的位置上。
     *
     * @return  This buffer
     */
    public final Buffer mark() {
        mark = position;
        return this;
    }

    /**
     * 将此缓冲区的位置重置为以前标记的位置。
     *
     * <p> 调用这个方法既不会改变也不会丢弃标记的值。</p>
     *
     * @return  This buffer
     *
     * @throws  InvalidMarkException
     *          If the mark has not been set
     */
    public final Buffer reset() {
        int m = mark;
        if (m < 0)
            throw new InvalidMarkException();
        position = m;
        return this;
    }

    /**
     * 清除这个缓冲区。
     * 位置被设置为零，限制被设置为容量，并且标记被丢弃。
     *
     * <p> 在使用一系列通道读取或放置操作来填充缓冲区之前调用此方法。例如:
     *
     * <blockquote><pre>
     * buf.clear();     // Prepare buffer for reading
     * in.read(buf);    // Read data</pre></blockquote>
     *
     * <p> 这个方法实际上并没有擦除缓冲区中的数据，但它被命名为好像它做了，
     * 因为它将最经常使用的情况下，是这种情况。</p>
     *
     * @return  This buffer
     */
    public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }

    /**
     * 翻转这个缓冲区。限制被设置为当前位置，然后位置被设置为零。
     * 如果标记已定义，则丢弃它。
     *
     * <p> 在一系列的通道读取或放置操作之后，调用这个方法来为一系列通道写入或相对获取操作做准备。例如:
     *
     * <blockquote><pre>
     * buf.put(magic);    // Prepend header
     * in.read(buf);      // Read data into rest of buffer
     * buf.flip();        // Flip buffer
     * out.write(buf);    // Write header + data to channel</pre></blockquote>
     *
     * <p> 在将数据从一个地方传输到另一个地方时，这种方法经常与compact方法一起使用。</p>
     *
     * @return  This buffer
     */
    public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

    /**
     * 倒带这个缓冲区。位置设置为零，标记被丢弃。
     *
     * <p> 在一系列channel-write或get操作之前调用此方法，假设已经适当地设置了限制。例如:
     *
     * <blockquote><pre>
     * out.write(buf);    // Write remaining data
     * buf.rewind();      // Rewind buffer
     * buf.get(array);    // Copy data into array</pre></blockquote>
     *
     * @return  This buffer
     */
    public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }

    /**
     * 返回当前位置和限制之间的元素数量。
     *
     * @return  The number of elements remaining in this buffer
     */
    public final int remaining() {
        return limit - position;
    }

    /**
     * 告诉当前位置和限制之间是否有任何元素。
     *
     * @return  <tt>true</tt> if, and only if, there is at least one element
     *          remaining in this buffer
     */
    public final boolean hasRemaining() {
        return position < limit;
    }

    /**
     * 告知此缓冲区是否为只读。
     *
     * @return  <tt>true</tt> if, and only if, this buffer is read-only
     */
    public abstract boolean isReadOnly();

    /**
     * 告知此缓冲区是否由可访问的数组支持。
     *
     * <p> 如果该方法返回true，则可以安全地调用array() 和 arrayOffset()方法。
     * </p>
     *
     * @return  <tt>true</tt> if, and only if, this buffer
     *          is backed by an array and is not read-only
     *
     * @since 1.6
     */
    public abstract boolean hasArray();

    /**
     * 返回支持这个缓冲区的数组(可选操作)。
     *
     * <p> 此方法旨在允许更有效地将数组支持的缓冲区传递给本机代码。
     * 具体的子类为这个方法提供了更强类型的返回值。
     *
     * <p> 对该缓冲区内容的修改将导致返回的数组的内容被修改，反之亦然。
     *
     * <p> 在调用这个方法之前调用hasArray方法，以确保这个缓冲区有一个可访问的支持的数组。</p>
     *
     * @return  The array that backs this buffer
     *
     * @throws  ReadOnlyBufferException
     *          If this buffer is backed by an array but is read-only
     *
     * @throws  UnsupportedOperationException
     *          If this buffer is not backed by an accessible array
     *
     * @since 1.6
     */
    public abstract Object array();

    /**
     * 返回缓冲区的第一个元素在缓冲区的后备数组中的偏移量(可选操作)。
     *
     * <p> 如果这个缓冲区是由一个数组支持的，那么缓冲区位置p相当于数组索引p + arrayOffset()。
     *
     * <p> 在调用这个方法之前调用hasArray方法，以确保这个缓冲区有一个可访问的后备数组。</p>
     *
     * @return  The offset within this buffer's array
     *          of the first element of the buffer
     *
     * @throws  ReadOnlyBufferException
     *          If this buffer is backed by an array but is read-only
     *
     * @throws  UnsupportedOperationException
     *          If this buffer is not backed by an accessible array
     *
     * @since 1.6
     */
    public abstract int arrayOffset();

    /**
     * 告诉该缓冲区是否直接。
     *
     * @return  <tt>true</tt> if, and only if, this buffer is direct
     *
     * @since 1.6
     */
    public abstract boolean isDirect();


    // -- 用于边界检查等的包私有方法。 --

    /**
     * 根据限制检查当前位置，如果它不小于限制，
     * 则抛出BufferUnderflowException，然后增加位置。
     *
     * @return  The current position value, before it is incremented
     */
    final int nextGetIndex() {                          // package-private
        if (position >= limit)
            throw new BufferUnderflowException();
        return position++;
    }

    final int nextGetIndex(int nb) {                    // package-private
        if (limit - position < nb)
            throw new BufferUnderflowException();
        int p = position;
        position += nb;
        return p;
    }

    /**
     * 检查当前位置是否符合限制，如果不小于限制，
     * 则抛出BufferOverflowException，然后增加位置。
     *
     * @return  The current position value, before it is incremented
     */
    final int nextPutIndex() {                          // package-private
        if (position >= limit)
            throw new BufferOverflowException();
        return position++;
    }

    final int nextPutIndex(int nb) {                    // package-private
        if (limit - position < nb)
            throw new BufferOverflowException();
        int p = position;
        position += nb;
        return p;
    }

    /**
     * 检查给定的索引是否符合限制，如果它不小于限制或小于零，
     * 则抛出IndexOutOfBoundsException异常。
     */
    final int checkIndex(int i) {                       // package-private
        if ((i < 0) || (i >= limit))
            throw new IndexOutOfBoundsException();
        return i;
    }

    final int checkIndex(int i, int nb) {               // package-private
        if ((i < 0) || (nb > limit - i))
            throw new IndexOutOfBoundsException();
        return i;
    }

    final int markValue() {                             // package-private
        return mark;
    }

    //注意：此操作，limit和capacity都为0，和clear不同
    final void truncate() {                             // package-private
        mark = -1;
        position = 0;
        limit = 0;
        capacity = 0;
    }

    final void discardMark() {                          // package-private
        mark = -1;
    }

    static void checkBounds(int off, int len, int size) { // package-private
        if ((off | len | (off + len) | (size - (off + len))) < 0)
            throw new IndexOutOfBoundsException();
    }

}
